/*
日志器模块:对日志消息格式化, 日志消息落地, 日志消息等级...几个模块进行整合, 以便于用户的使用

成员:
    1.格式化模块对象
    2.落地模块对象数组(一个日志器可能会向多个位置进行日志输出)
    3.默认的日志输出限制等级(大于等于限制等级的日志才会输出)
    4.互斥锁
    5.日志器的名称(日志器的唯一标识)
操作:
    DEBUG等级日志的输出操作
    INFO等级日志的输出操作
    ...
实现:
    Logger抽象类
        - 同步日志器
        - 异步日志器
    [二者的落地方式不同:磁盘or内存]

    落地方向抽象类
        - 同步日志器往磁盘写入
        - 异步日志器往内存写入
*/
#ifndef __M_LOGGER_H__
#define __M_LOGGER_H__
// #define _GNU_SOURCE /* See feature_test_macros(7) */

#include "util.hpp"
#include "level.hpp"
#include "message.hpp"
#include "formatter.hpp"
#include "sink.hpp"
#include "looper.hpp"
#include <vector>
#include <list>
#include <atomic>
#include <unordered_map>
#include <cstdarg>
#include <unordered_map>
#include <type_traits>

namespace LOG
{
    class Logger
    {
    public:
        enum class Type
        {
            LOGGER_SYNC = 0,
            LOGGER_ASYNC
        };
        using ptr = std::shared_ptr<Logger>;
        // TODO
        Logger(const std::string &name, Formatter::ptr &formatter,
               std::vector<LogSink::ptr> &sinks, LogLevel::value level = LogLevel::value::DEBUG) : _logger_name(name), _formatter(formatter), _sinks(sinks), _limit_level(level)
        {
        }
        const std::string &name() { return _logger_name; }
        // 完成构造日志消息对象并且进行格式化, 得到格式化之后的日志消息字符串--然后进行落地输出
        /*
        通过传入的参数构造出日志消息对象, 进行日志的格式化, 最终落地
            1.判断当前的日志是否达到了输出等级
            2.对fmt格式化字符串和不定参进行字符串组织, 得到的日志消息的字符串
            3.构造LogMsg对象
            4.通过格式化工具对LogMsg进行格式化, 得到格式化后的日志字符串
            5.进行日志落地
        */
        void debug(const std::string &file, size_t line, const char *fmt, ...)
        {
            // 1.
            if (LogLevel::value::DEBUG < _limit_level)
                return;
            // 2.
            va_list ap;
            va_start(ap, fmt);
            char *res; // res中存放日志的主体消息
            int ret = vasprintf(&res, fmt, ap);
            if (ret == -1)
            {
                std::cout << "vasprintf failed!\n";
                return;
            }
            va_end(ap);
            serialize(LogLevel::value::DEBUG, file, line, res);
            // 释放资源
            free(res);
        }
        void info(const std::string &file, size_t line, const char *fmt, ...)
        {
            // 1.
            if (LogLevel::value::INFO < _limit_level)
                return;
            // 2.
            va_list ap;
            va_start(ap, fmt);
            char *res; // res中存放日志的主体消息
            int ret = vasprintf(&res, fmt, ap);
            if (ret == -1)
            {
                std::cout << "vasprintf failed!\n";
                return;
            }
            va_end(ap);
            serialize(LogLevel::value::INFO, file, line, res);
            // 释放资源
            free(res);
        }
        void warn(const std::string &file, size_t line, const char *fmt, ...)
        {
            // 1.
            if (LogLevel::value::WARN < _limit_level)
                return;
            // 2.
            va_list ap;
            va_start(ap, fmt);
            char *res; // res中存放日志的主体消息
            int ret = vasprintf(&res, fmt, ap);
            if (ret == -1)
            {
                std::cout << "vasprintf failed!\n";
                return;
            }
            va_end(ap);
            serialize(LogLevel::value::WARN, file, line, res);
            // 释放资源
            free(res);
        }
        void error(const std::string &file, size_t line, const char *fmt, ...)
        {
            // 1.
            if (LogLevel::value::ERROR < _limit_level)
                return;
            // 2.
            va_list ap;
            va_start(ap, fmt);
            char *res; // res中存放日志的主体消息
            int ret = vasprintf(&res, fmt, ap);
            if (ret == -1)
            {
                std::cout << "vasprintf failed!\n";
                return;
            }
            va_end(ap);
            serialize(LogLevel::value::ERROR, file, line, res);
            // 释放资源
            free(res);
        }
        void fatal(const std::string &file, size_t line, const char *fmt, ...)
        {
            // 1.
            if (LogLevel::value::FATAL < _limit_level)
                return;
            // 2.
            va_list ap;
            va_start(ap, fmt);
            char *res; // res中存放日志的主体消息
            int ret = vasprintf(&res, fmt, ap);
            if (ret == -1)
            {
                std::cout << "vasprintf failed!\n";
                return;
            }
            va_end(ap);
            serialize(LogLevel::value::FATAL, file, line, res);
            // 释放资源
            free(res);
        }

    protected:
        /*抽象落地输出--不同的日志器会有不同的实际落地方式*/
        virtual void log(const char *data, size_t len) {}

        void serialize(LogLevel::value level, const std::string &file, size_t line, char *res)
        {
            // 3.
            LogMsg msg(_logger_name, file, line, res, level);
            // 4.
            std::string str;
            str = _formatter->format(msg);
            // 5.日志落地操作
            log(str.c_str(), str.size());
        }

    protected:
        std::mutex _mutex;
        std::string _logger_name;
        std::atomic<LogLevel::value> _limit_level;
        Formatter::ptr _formatter;
        // 日志落地方向
        std::vector<LogSink::ptr> _sinks;
    };

    // 同步日志器
    class SyncLogger : public Logger
    {
    public:
        SyncLogger(const std::string &name, Formatter::ptr &formatter,
                   std::vector<LogSink::ptr> &sinks, LogLevel::value level = LogLevel::value::DEBUG) : Logger(name, formatter, sinks, level)
        {
        }

    protected:
        // 同步日志器, 将日志直接通过落地模块句柄直接进行日志落地
        void log(const char *data, size_t len)
        {
            std::unique_lock<std::mutex> lock(_mutex);
            if (_sinks.empty())
                return;
            for (auto &sink : _sinks)
            {
                sink->log(data, len);
            }
        }
    };
    // 异步日志器
    class AsyncLogger : public Logger
    {
    public:
        AsyncLogger(const std::string &name, Formatter::ptr &formatter,
                    std::vector<LogSink::ptr> &sinks, AsyncType looper_type, LogLevel::value level = LogLevel::value::DEBUG) : Logger(name, formatter, sinks, level),
                                                                                                                               _looper(std::make_shared<AsyncLooper>(std::bind(&AsyncLogger::realLog, this, std::placeholders::_1), looper_type))
        {
        }
        void log(const char *data, size_t len) // 将数据写入缓冲区
        {
            _looper->push(data, len);
        }
        // 设计一个实际落地函数(将缓冲区中的数据落地)
        void realLog(Buffer &buf)
        {
            if (_sinks.empty())
                return;
            for (auto &sink : _sinks)
            {
                // printf("realLog:%s\n",buf.begin());
                sink->log(buf.begin(), buf.readAbleSize());
            }
        }

    private:
        AsyncLooper::ptr _looper;
    };

    // 使用建造者模式来建造日志器, 而不是让用户直接构造日志器, 简化用户的使用复杂度
    /*1. 抽象一个日志器建造者类
        - 设置日志器类型
        - 将不同类型的日志器的创建操作放到同一个日志器建造类中完成
    */

    enum class LoggerType // 日志器类型
    {
        LOGGER_SYNC,
        LOGGER_ASYNC
    };

    class LoggerBuilder
    {
    public:
        LoggerBuilder() : _logger_type(LoggerType::LOGGER_SYNC),
                          _limit_level(LogLevel::value::DEBUG),
                          _looper_type(AsyncType::ASYNC_SAFE) {}
        void buildLoggerType(LoggerType type) { _logger_type = type; }
        void buildEnableUnSafeAsync() { _looper_type = AsyncType::ASYNC_UNSAFE; }
        void buildLoggerName(const std::string &name) { _logger_name = name; }
        void buildLoggerLevel(LogLevel::value level) { _limit_level = level; }
        void buildLoggerFormat(const std::string &pattern)
        {
            _formatter = std::make_shared<LOG::Formatter>(pattern);
        }
        void buildLoggerFormat(const Formatter::ptr &formatter)
        {
            _formatter = formatter;
        }
        template <typename SinkType, typename... Args>
        void buildSink(Args &&...args)
        {
            LogSink::ptr psink = SinkFactory::create<SinkType>(std::forward<Args>(args)...);
            _sinks.push_back(psink);
        }

        virtual Logger::ptr build() = 0;

    protected:
        AsyncType _looper_type;
        LoggerType _logger_type;
        std::string _logger_name; // 日志器的唯一标识
        LogLevel::value _limit_level;
        Formatter::ptr _formatter;
        std::vector<LogSink::ptr> _sinks;
    };

    /*2. 派生出具体的建造者类
        - 局部日志器的建造者
        - 全局日志器的建造者
    */
    class LocalLoggerBuilder : public LoggerBuilder
    {
    public:
        Logger::ptr build() override
        {
            assert(!_logger_name.empty()); // 必须要有日志器名称
            if (_formatter.get() == nullptr)
            {
                _formatter = std::make_shared<Formatter>();
            }

            if (_sinks.empty())
            {
                // 如果没有落地器, 则添加一个标准输出的落地器
                LogSink::ptr psink = SinkFactory::create<LOG::StdoutSink>();
                _sinks.push_back(psink);
            }

            if (_logger_type == LoggerType::LOGGER_ASYNC) // 如果是异步日志器
            {
                return std::make_shared<AsyncLogger>(_logger_name, _formatter, _sinks, _looper_type, _limit_level);
            }
            return std::make_shared<SyncLogger>(_logger_name, _formatter, _sinks, _limit_level);
        }
    };
    /*
    单例模式下的日志器管理器:
    - 对所有创建的日志器进行管理
    - 获取相同的单例对象, 进行日志打印输出
    - 用户在不创建任何日志器的情况下, 也能进行标准的打印输出

    实现:
        成员:
        1. 默认日志器
        2. 所管理的日志器数组
        3. 互斥锁
        接口:
        1. 添加日志器管理
        2. 判断是否管理了指定名称的日志器
        3. 获取指定名称的日志器
        4. 获取默认日志器
    */
    class loggerManager
    {
    public:
        static loggerManager &getInstance()
        {
            // 在C++11之后, 针对静态局部变量, 编译器在编译层面实现了线程安全
            static loggerManager eton;
            return eton;
        }
        // 添加日志器
        void addLogger(Logger::ptr &logger)
        {
            if (hasLogger(logger->name()))
                return;
            std::unique_lock<std::mutex> lock(_mutex);
            _loggers.insert({logger->name(), logger});
        }
        // 判断是否管理了指定的日志器
        bool hasLogger(const std::string &name)
        {
            std::unique_lock<std::mutex> lock(_mutex);
            auto it = _loggers.find(name);
            if (it == _loggers.end())
            {
                return false;
            }
            return true;
        }
        // 获取指定的日志器
        Logger::ptr getLogger(const std::string &name)
        {
            std::unique_lock<std::mutex> lock(_mutex);
            auto it = _loggers.find(name);
            if (it == _loggers.end())
            {
                return Logger::ptr();
            }
            return it->second;
        }
        // 获取默认的日志器
        Logger::ptr rootLogger()
        {
            return _root_logger;
        }

    private:
        loggerManager()
        {
            // 默认日志器构造
            std::unique_ptr<LOG::LoggerBuilder> builder(new LOG::LocalLoggerBuilder()); //因为在全局建造器中需要添加单例对象, 因此会陷入死循环
            builder->buildLoggerName("root");
            _root_logger = builder->build();
            _loggers.insert({_root_logger->name(), _root_logger});
        }

    private:
        std::mutex _mutex;
        Logger::ptr _root_logger; // 默认日志器
        std::unordered_map<std::string, Logger::ptr> _loggers;
    };

    class GlobalLoggerBuilder : public LoggerBuilder
    {
    public:
        Logger::ptr build() override
        {
            assert(!_logger_name.empty()); // 必须要有日志器名称
            if (_formatter.get() == nullptr)
            {
                _formatter = std::make_shared<Formatter>();
            }

            if (_sinks.empty())
            {
                // 如果没有落地器, 则添加一个标准输出的落地器
                LogSink::ptr psink = SinkFactory::create<LOG::StdoutSink>();
                _sinks.push_back(psink);
            }
            Logger::ptr logger;
            if (_logger_type == LoggerType::LOGGER_ASYNC) // 如果是异步日志器
            {
                logger = std::make_shared<AsyncLogger>(_logger_name, _formatter, _sinks, _looper_type, _limit_level);
            }
            else
            {
                logger = std::make_shared<SyncLogger>(_logger_name, _formatter, _sinks, _limit_level);
            }
            loggerManager::getInstance().addLogger(logger);
            return logger;
        }
    };
}
#endif